﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;

namespace GBricks
{
  using Collections;

  partial class Lambda
  {
    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Memoize", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden]
    public static Func<T, TResult> Memoize<T, TResult>(this Func<T, TResult> factory, IEqualityComparer<T> comparer = null, int capacity = 0) {
      Argument.NotNull(factory, "factory");
      Contract.Ensures(Contract.Result<Func<T, TResult>>() != null);
      var cache = new Dictionary<T, TResult>(capacity, comparer);
      return x => cache.GetOrAdd(x, factory);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Memoize", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden]
    public static Func<T, TResult> Memoize<T, TValue, TResult>(this Func<T, TValue, TResult> factory, TValue value, IEqualityComparer<T> comparer = null, int capacity = 0) {
      Argument.NotNull(factory, "factory");
      Contract.Ensures(Contract.Result<Func<T, TResult>>() != null);
      var cache = new Dictionary<T, TResult>(capacity, comparer);
      return x => cache.GetOrAdd(x, y => factory(y, value));
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Memoize", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden]
    public static Func<T, TValue, TResult> Memoize<T, TValue, TResult>(this Func<T, TValue, TResult> factory, IEqualityComparer<T> comparer = null, int capacity = 0) {
      Argument.NotNull(factory, "factory");
      Contract.Ensures(Contract.Result<Func<T, TValue, TResult>>() != null);
      var cache = new Dictionary<T, TResult>(capacity, comparer);
      return (x, v) => cache.GetOrAdd(x, y => factory(y, v));
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Memoize", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden]
    public static Func<T, TResult> MemoizeConcurrent<T, TResult>(this Func<T, TResult> factory, IEqualityComparer<T> comparer = null, int capacity = 0, int? concurrencyLevel = null) {
      Argument.NotNull(factory, "factory");
      Contract.Ensures(Contract.Result<Func<T, TResult>>() != null);
      var cache = CreateConcurrentDictionary<T, TResult>(comparer, capacity, concurrencyLevel);
      return x => cache.GetOrAdd(x, factory);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Memoize", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden]
    public static Func<T, TResult> MemoizeConcurrent<T, TValue, TResult>(this Func<T, TValue, TResult> factory, TValue value, IEqualityComparer<T> comparer = null, int capacity = 0, int? concurrencyLevel = null) {
      Argument.NotNull(factory, "factory");
      Contract.Ensures(Contract.Result<Func<T, TResult>>() != null);
      var cache = CreateConcurrentDictionary<T, TResult>(comparer, capacity, concurrencyLevel);
      return x => cache.GetOrAdd(x, y => factory(y, value));
    }

    [DebuggerHidden]
    private static ConcurrentDictionary<TKey, TValue> CreateConcurrentDictionary<TKey, TValue>(IEqualityComparer<TKey> comparer = null, int capacity = 0, int? concurrencyLevel = null) {
      Contract.Ensures(Contract.Result<ConcurrentDictionary<TKey, TValue>>() != null);
      return concurrencyLevel != null
          ? new ConcurrentDictionary<TKey, TValue>(concurrencyLevel.Value, capacity, comparer)
          : new ConcurrentDictionary<TKey, TValue>(comparer);
    }
  }
}
